package io;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.jdom2.DataConversionException;
import org.jdom2.Element;
import org.jdom2.JDOMException;

import ui.MainPage;
import users.Role;
import users.User;

/**
 * This class provides access to User for the peer review system.
 * 
 * @author James Kantak
 * @author Lun-Wei Chang
 * @author Aaron Chen
 * 
 * @version March 9, 2014
 * 
 */
public class UserAccess extends AbstractAccess {
  
  /** The user id's element position. */
  public static final int USER_ID = 0;
  /** The user's first name element position. */
  public static final int USERFIRSTNAME = 1;
  /** The user last name element position. */
  public static final int USERLASTNAME = 2;
  /** The user email element position. */
  public static final int USER_EMAIL = 3;
  /** The user conference id element position. */
  public static final int USER_CONFERENCE_ID = 4;
  /** The user conference title element position. */
  public static final int USER_CONFERENCE_TITLE = 5;
  /** The user role id element position. */
  public static final int USER_ROLE_ID = 6;
  /** The user conference description element position. */
  public static final int USER_CONFERENCE_DESCRIPTION = 7;
  /** The user role element position. */
  public static final int USER_ROLE = 8;
  
  /** The name of the XML file. */
  private static final String XML_FILE = "User.xml";
  
  /** A constant for the user id attribute in the XML file. */
  private static final String ATTRIBUTE_ID = "id";
  
  /** A constant for the user id element in the XML file. */
  private static final String USER_USERID = "UserID";
  
  /** A constant for the first name element in the XML file. */
  private static final String USER_FIRSTNAME = "fName";
  
  /** A constant for the last name element in the XML file. */
  private static final String USER_LASTNAME = "lName";
  
  /** A constant for the email element in the XML file. */
  private static final String USER_USEREMAIL = "email";
  
  /** A constant for the roles element in the XML file. */
  private static final String USER_ROLES = "Roles";
  
  /** A constant for the individual role elements in the XML file. */
  private static final String ROLE = "Role";
  
  /** A constant for the individual role ID elements in the XML file. */
  private static final String ROLE_ID = "RoleID";
  
  
  /**
   * The constructor to instantiate the UserAccess objects.
   * @throws IOException If there is a problem establishing connectivity to the file.
   * @throws JDOMException If the file cannot be correctly parsed.
   */
  public UserAccess() throws IOException, JDOMException {
    super(XML_FILE);
  }
  
  /**
   * Returns a specific User.
   * 
   * @param the_id The identification number unique to each User.
   * @return A single User.
   */
  public User getUser(final long the_id) {
    User u = null;
    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren(USER_USERID);
    for (Element e : list) {
      try {
        if (e.getAttribute(ATTRIBUTE_ID).getLongValue() == the_id) {
          u = dataToUser(e);
          break;
        }
      } catch (final DataConversionException err) {
        err.printStackTrace();
      }
    }
    return u;
  }
  
  /**
   * Returns a specific User.
   * 
   * @param the_first The User's first name.
   * @param the_last The User's last name
   * @return A single User, or null if no such user exists.
   */
  public User getUser(final String the_first, final String the_last) {
    User u = null;
    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren(USER_USERID);
    for (Element e : list) {
      if (the_first.equals(e.getChildText(USER_FIRSTNAME))
          && the_last.equals(e.getChildText(USER_LASTNAME))) {
        u = dataToUser(e);
        break;
      }
    }
    return u;
  }

  /**
   * Returns all the users.
   * 
   * @return A list of users.
   */
  public List<User> getAllUser() {

    /** An array list to hold the manuscript's information. */
    final List<User> userdata = new ArrayList<User>();

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    for (Element e : list) {
      userdata.add(dataToUser(e));
    }
    return userdata;
  }

  /**
   * Writes a new manuscript entry into the database.
   * 
   * @param the_u A new manuscript.
   * @return true if the operation was a success; false otherwise.
   */
  public boolean createUser(final User the_u) {
    boolean result = true;

    final Element rootnode = getDoc().getRootElement();
    final Element e = userToData(the_u, false);
    rootnode.addContent(e);

    // If the write operation has failed, then remove the node so that
    // subsequent write operations do not include this node.
    result = writeToDatabase();
    if (!result) {
      e.detach();
    }

    return result;
  }

  /**
   * Writes over a pre-existing user in the database.
   * 
   * @param the_u The User data to write into the database.
   * @return true if the user to edit was found and overwritten; false
   *         otherwise.
   */
  public boolean editUser(final User the_u) {
    boolean result = false;

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    Element old_element = null;
    final Element new_element = userToData(the_u, true);
    for (Element e : list) {
      if (Long.parseLong(e.getAttributeValue(ATTRIBUTE_ID)) == the_u.getID()) {
        old_element = e.detach();
        rootnode.addContent(new_element);
        result = true;
        break;
      }
    }

    // If the write operation has failed, then remove the node so that
    // subsequent write operations do not include this node.
    if (result) {
      result = writeToDatabase();
      if (!result) {
        rootnode.addContent(old_element);
        new_element.detach();
      }
    }

    return result;
  }

  /**
   * Deletes the user from the database.
   * @param the_id The ID of the user.
   * @return true if the user to edit was found and overwritten; false
   *         otherwise.
   */
  public boolean deleteUser(final long the_id) {
    boolean result = false;

    final Element rootnode = getDoc().getRootElement();
    final List<Element> list = rootnode.getChildren();
    Element element = null;
    for (Element e : list) {
      if (Long.parseLong(e.getAttributeValue(ATTRIBUTE_ID)) == the_id) {
        element = e.detach();
        result = true;
        break;
      }
    }
    if (result) {
      result = writeToDatabase();
      if (!result) {
        rootnode.addContent(element);
      }
    }

    return result;
  }

  /**
   * Translates the XML element data into a User object.
   * 
   * @param the_e The element that contains User data.
   * @return a User object.
   */
  private User dataToUser(final Element the_e) {
    final List<String> fields = new ArrayList<String>();
    fields.add(the_e.getAttributeValue(ATTRIBUTE_ID));
    fields.add(the_e.getChildText(USER_FIRSTNAME));
    fields.add(the_e.getChildText(USER_LASTNAME));
    fields.add(the_e.getChildText(USER_USEREMAIL));
    
    final Element er = the_e.getChild(USER_ROLES);
    final List<Role> roles = new ArrayList<Role>();
    if (er != null) {
      for (Element e : er.getChildren()) {
        roles.add(dataToRole(e));
      }
    }
    
    final User u = new User(fields, roles);
    
    return u;
  }

  /**
   * Translates the User object into XML element data.
   * 
   * @param the_u The User object to translate.
   * @param the_edit True if this is an edit operation; false if it is a new
   *          entry.
   * @return an XML element node.
   */
  private Element userToData(final User the_u, final boolean the_edit) {
    final Element usernode = new Element(USER_USERID);

    if (!the_edit) {
      final long id = getNextID();
      usernode.setAttribute(ATTRIBUTE_ID, Long.toString(id));
      the_u.setID(id);
    } else {
      usernode.setAttribute(ATTRIBUTE_ID, Long.toString(the_u.getID()));
    }

    usernode.addContent(new Element(USER_FIRSTNAME).setText(the_u.getFirstName()));
    usernode.addContent(new Element(USER_LASTNAME).setText(the_u.getLastName()));
    usernode.addContent(new Element(USER_USEREMAIL).setText(the_u.getEmail()));
    
    final Element role_node = new Element(USER_ROLES);
    final List<Role> roles = the_u.getRoles();
    if (roles != null) {
      for (Role r : roles) {
        role_node.addContent(roleToData(r, false));
      }
    }
    usernode.addContent(role_node);

    return usernode;
  }
  
  /**
   * Translates the Role object into XML element data.
   * @param the_e    The element that contains Role data.
   * @return an XML element node.
   */
  private Role dataToRole(final Element the_e) {
    final long role_id = Long.parseLong(the_e.getText());
    final RoleAccess database = MainPage.getDatabase().accessRole();
    final Role role = database.getRole(role_id);
    return role;
  }
  
  /**
   * Translates the Role object into XML element data.
   * @param the_r    The Role object to translate.
   * @param the_edit Not used.
   * @return an XML element node.
   */
  private Element roleToData(final Role the_r, final boolean the_edit) {
    final Element rolenode = new Element(ROLE_ID).setText(Long.toString(the_r.getID()));
    return rolenode;
  }
  
}
